/* 
 * Copyright (C) 2011 rccloud.com All Rights Reserved.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package com.rccloud.inproxy.utils;

import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.List;

/**
 * Reflection operation utilities
 */
public class ReflectionUtils {

	/**
	 * Get the method name
	 * 
	 * @param fieldName
	 *            field name
	 * @param prefix
	 *            the prefix
	 * @return method name
	 */
	public static String getMethodName(String fieldName, String prefix) {
		if (prefix == null || prefix.equals(""))
			return fieldName;
		return prefix + fieldName.substring(0, 1).toUpperCase()
				+ fieldName.substring(1);
	}

	/**
	 * Return set method of a field
	 * 
	 * @param targetClass
	 *            target class
	 * @param field
	 *            field info
	 * @return set method
	 */
	public static Method getSetMethod(Class<?> targetClass, Field field) {
		String setMethodName = getMethodName(field.getName(), "set");
		try {
			return targetClass.getMethod(setMethodName, field.getType());
		} catch (Exception e) {
		}
		return null;
	}

	/**
	 * Return get method of a field
	 * 
	 * @param targetClass
	 *            target class
	 * @param field
	 *            field info
	 * @return get method
	 */
	public static Method getGetMethod(Class<?> targetClass, Field field) {
		String getMethodName1 = getMethodName(field.getName(), "get");
		String getMethodName2 = getMethodName(field.getName(), "is");

		if (field.getType().equals(boolean.class)
				|| field.getType().equals(Boolean.class)) {
			try {
				return targetClass.getMethod(getMethodName2);
			} catch (Exception e) {
			}
		}
		try {
			return targetClass.getMethod(getMethodName1);
		} catch (Exception e) {
		}
		return null;
	}

	/**
	 * Get field info
	 * 
	 * @param targetClass
	 *            target class
	 * @param fieldName
	 *            field name
	 * @param originalFieldName
	 *            default field name, if field name doesn't exists, it will use
	 *            this name instead.
	 * @return field info
	 */
	public static Field getField(Class<?> targetClass, String fieldName,
			String originalFieldName) {
		Field field = searchFieldInParents(targetClass, fieldName, Object.class);
		if (field == null) {
			return searchFieldInParents(targetClass, originalFieldName,
					Object.class);
		}
		return field;
	}

	/**
	 * Get all fields in a class
	 * 
	 * @param targetClass
	 *            target class
	 * @param stopClass
	 *            the parent class, get fields method will stop at this class.
	 * @return all fields info
	 */
	public static Field[] getAllFields(Class<?> targetClass, Class<?> stopClass) {
		List<Field> fields = new ArrayList<Field>();
		Class<?> clazz = targetClass;
		while (clazz != null && !clazz.equals(stopClass)) {
			Field[] fs = clazz.getDeclaredFields();
			for (Field f : fs) {
				fields.add(f);
			}
			clazz = clazz.getSuperclass();
		}
		return fields.toArray(new Field[] {});
	}

	/**
	 * Search the field in class and it's parent class
	 * 
	 * @param targetClass
	 *            the class to be searched
	 * @param fieldName
	 *            field name
	 * @param stopClass
	 *            the parent class for stopping search
	 * @return filed info
	 */
	public static Field searchFieldInParents(Class<?> targetClass,
			String fieldName, Class<?> stopClass) {
		Class<?> clazz = targetClass;
		while (clazz != null && !clazz.equals(stopClass)) {
			Field field = null;
			try {
				field = clazz.getDeclaredField(fieldName);
			} catch (Exception ex) {
			}
			if (field != null)
				return field;
			clazz = clazz.getSuperclass();
		}
		return null;
	}

}